#pragma once
// 实现语音识别子服务
/*
    1.包含语音识别客户端
    2.包含brpc服务器，其中实现SpeechServiceImpl为调用语音客户端对语音信息进行识别
    3.服务器注册客户端etcd
*/
#include <brpc/server.h>
#include <butil/logging.h>
#include "logger.hpp" //日志模块封装
#include "etcd.hpp"   //服务注册模块封装
#include "data_es.hpp" //es数据管理客户端的封装
#include "data_redis.hpp" //redis数据管理客户端的封装
#include "data_mysql.hpp" //mysql数据管理客户端的封装
#include "user.pb.h"  //protobuf框架代码
#include "base.pb.h"  
#include "file.pb.h"  //文件的上传和下载
#include "user.hxx"
#include "user-odb.hxx"
#include "utils.hpp"
#include "dms.hpp"
#include "channel.hpp"

/*
    用户管理子模块相比文件管理子模块多了信道管理对象、服务发现对象、文件子服务名称
    因为头像是存储在文件系统的，通过文件服务名称在信道管理对象找到文件子服务的信道
    调用文件子服务进行通信，获取头像

    服务发现对象没用到

    ServiceManager::ptr _mm_channel; //RPC信道管理对象,进行文件子服务的rpc，请求进行头像的文件下载
    std::string _file_name_service; //文件子服务的名称
    Discovery::ptr _service_discoverer;                 //服务发现对象
*/

namespace zrb
{
    // （1）.创建rpc服务子类继承pb中的EchoService服务类，并实现内部的业务接口逻辑
    class UserServiceImpl : public zrb::UserService
    {
    public:
        UserServiceImpl(DMSClient::ptr dms_client,
            std::shared_ptr<odb::core::database> _mysql_odb,
            std::shared_ptr<elasticlient::Client> _es_client,
            std::shared_ptr<sw::redis::Redis> _redis_client,
            const ServiceManager::ptr& mm_channel,
            const std::string& file_name_service)
            : _mysql_handle(std::make_shared<userTable>(_mysql_odb))
            , _es_handle(std::make_shared<ESUser>(_es_client))
            , _session_handle(std::make_shared<Session>(_redis_client))
            , _status_handle(std::make_shared<Status>(_redis_client))
            , _code_handle(std::make_shared<Code>(_redis_client))
            , _mm_channel(mm_channel)
            , _file_name_service(file_name_service)
            ,_dms_client(dms_client)
        {
        }

        ~UserServiceImpl()
        {
        }

        //检测用户名
        bool checkNickname(const std::string& nickname)
        {
            return nickname.size() < 22;
        }

        //检测电话号码
        bool checkPhone(const std::string& phone)
        {
            if(phone.size() != 11)  return false;
            if(phone[0] != '1') return false;
            if(phone[1] < '3' || phone[1] > '9') return false;
            for(int i = 2; i < phone.size(); ++i)
            {
                if(phone[i] < '0' || phone[i] > '9')
                {
                    return false;
                }
            }
            return true;
        }

        //检测密码
        bool checkPassword(const std::string& password)
        {
            if(password.size() < 6 || password.size() > 15)
            {
                LOG_ERROR("密码长度不合法 {}-{}",password,password.size());
                return false;
            }
            for(int i = 0; i < password.size(); ++i)
            {
                if(!(password[i] >= 'a' && password[i] <= 'z' ||
                    password[i] >= 'A' && password[i] <= 'Z' ||
                    password[i] >= '0' && password[i] <= '9' ||
                    password[i] == '-' ||
                    password[i] == '_'))
                {
                    LOG_ERROR("密码不合法 {}",password);
                    return false;
                }
            }
            return true;
        }

        void UserRegister(google::protobuf::RpcController *controller,
                          const ::zrb::UserRegisterReq *request,
                          ::zrb::UserRegisterRsp *response,
                          ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            //定义一个错误回调函数
            auto err_response = [this,response](const std::string& id,const std::string& errmsg){
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1. 从请求中取出昵称和密码
            std::string nickname = request->nickname();
            std::string password = request->password();
            //2. 检查昵称是否合法（长度限制 3~15 之间）
            bool ret = checkNickname(nickname);
            if(ret == false)
            {
                LOG_ERROR("昵称格式不合法");
                return err_response(request->request_id(),"用户名不合法!");
            }
            //3. 检查密码是否合法（只能包含字母，数字，长度限制 6~15 之间）
            ret = checkPassword(password);
            if(ret == false)
            {
                LOG_ERROR("密码格式不合法");
                return err_response(request->request_id(),"密码格式不合法!");
            }
            //4. 根据昵称在数据库进行判断是否昵称已存在
            auto user = _mysql_handle->select_by_nickname(nickname);
            if(user)
            {
                LOG_ERROR("用户名被占用");
                return err_response(request->request_id(),"用户名被占用!");
            }
            //5. 向数据库新增数据
            std::string uid = uuid();
            user = std::make_shared<User>(uid,nickname,password);
            ret = _mysql_handle->insert(user);
            if(ret == false)
            {
                LOG_ERROR("mysql新增数据失败");
                return err_response(request->request_id(),"mysql新增数据失败!");
            }
            //6. 向 ES 服务器中新增用户信息
            ret = _es_handle->appendData(uid,nickname,"","","");
            if(ret == false)
            {
                LOG_ERROR("es搜索引擎新增数据失败");
                return err_response(request->request_id(),"es搜索引擎新增数据失败!");
            }
            //7. 组织响应，进行成功与否的响应即可
            response->set_request_id(request->request_id());
            response->set_success(true);
        }

        void UserLogin(google::protobuf::RpcController *controller,
                       const ::zrb::UserLoginReq *request,
                       ::zrb::UserLoginRsp *response,
                       ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            //定义一个错误回调函数
            auto err_response = [this,response](const std::string& id,const std::string& errmsg){
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1. 从请求中取出昵称和密码
            std::string nickname = request->nickname();
            std::string password = request->password();
            //2. 通过昵称获取用户信息，进行密码是否一致的判断
            auto user = _mysql_handle->select_by_nickname(nickname);
            if(!user || user->password() != password)
            {
                LOG_ERROR("用户名或者密码错误 ： {}-{}",nickname,password);
                return err_response(request->request_id(),"用户名或者密码错误!");
            }
            //3. 根据 redis 中的登录标记信息是否存在判断用户是否已经登录。
            auto ret = _status_handle->exist(user->user_id());
            if(ret == true)
            {
                LOG_ERROR("用户已在其他地方登录");
                return err_response(request->request_id(),"用户已在其他地方登录!");
            }
            //4. 构造会话 ID，生成会话键值对，向 redis 中添加会话信息以及登录标记信息
            std::string ssid = uuid();
            _session_handle->append(ssid,user->user_id());
            //5. 添加用户登录信息
            _status_handle->append(user->user_id());
            //6. 组织响应，返回生成的会话 ID
            response->set_request_id(request->request_id());
            response->set_success(true);
            response->set_login_session_id(ssid);
        }

        void GetPhoneVerifyCode(google::protobuf::RpcController *controller,
                                const ::zrb::PhoneVerifyCodeReq *request,
                                ::zrb::PhoneVerifyCodeRsp *response,
                                ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            //定义一个错误回调函数
            auto err_response = [this,response](const std::string& id,const std::string& errmsg){
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1. 从请求中取出手机号码
            std::string phone = request->phone_number();
            //2. 验证手机号码格式是否正确（必须以 1 开始，第二位 3~9 之间，后边 9 个数字字符）
            bool ret = checkPhone(phone);
            if(ret == false)
            {
                LOG_ERROR("手机号码格式错误");
                return err_response(request->request_id(),"手机号码格式错误!");
            }
            //3. 生成 4 位随机验证码
            std::string code = vcode();
            //4. 基于短信平台 SDK 发送验证码
            ret = _dms_client->send(phone,code);
            if(ret == false)
            {
                LOG_ERROR("验证码发送失败");
                return err_response(request->request_id(),"验证码发送失败!");
            }
            //5. 构造验证码 ID，添加到 redis 验证码映射键值索引中
            std::string code_id = uuid();
            _code_handle->append(code_id,code);
            //6. 组织响应，返回生成的验证码 ID
            response->set_request_id(request->request_id());
            response->set_success(true);
            response->set_verify_code_id(code_id);
        }

        void PhoneRegister(google::protobuf::RpcController *controller,
                           const ::zrb::PhoneRegisterReq *request,
                           ::zrb::PhoneRegisterRsp *response,
                           ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            //定义一个错误回调函数
            auto err_response = [this,response](const std::string& id,const std::string& errmsg){
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1. 从请求中取出手机号码和验证码
            std::string phone = request->phone_number();
            std::string code_id = request->verify_code_id();
            std::string code = request->verify_code();
            //2. 检查注册手机号码是否合法
            bool ret = checkPhone(phone);
            if(ret == false)
            {
                LOG_ERROR("手机号码格式错误");
                return err_response(request->request_id(),"手机号码格式错误!");
            }
            //3. 从 redis 数据库中进行验证码 ID-验证码一致性匹配
            auto vcode = _code_handle->uid(code_id);
            if(vcode != code)
            {
                LOG_ERROR("验证码错误 {}-{}",phone,code);
                return err_response(request->request_id(),"验证码错误!");
            }
            //4. 通过数据库查询判断手机号是否已经注册过
            auto user = _mysql_handle->select_by_phone(phone);
            if(user)
            {
                LOG_ERROR("该手机号已经注册过了 ： {}",phone);
                return err_response(request->request_id(),"该手机号已经注册过了!");
            }
            //5. 向数据库新增用户信息
            std::string uid = uuid();
            ret = _mysql_handle->insert(std::make_shared<User>(uid,phone));
            if(ret == false)
            {
                LOG_ERROR("向数据库新增用户信息失败");
                return err_response(request->request_id(),"向数据库新增用户信息失败!");
            }
            //6. 向 ES 服务器中新增用户信息
            ret = _es_handle->appendData(uid,"",phone,"","");
            if(ret == false)
            {
                LOG_ERROR("es搜索引擎新增数据失败");
                return err_response(request->request_id(),"es搜索引擎新增数据失败!");
            }
            //7. 组织响应，进行成功与否的响应即可
            response->set_request_id(request->request_id());
            response->set_success(true);
        }

        void PhoneLogin(google::protobuf::RpcController *controller,
                        const ::zrb::PhoneLoginReq *request,
                        ::zrb::PhoneLoginRsp *response,
                        ::google::protobuf::Closure *done)
        {
            //定义一个错误回调函数
            auto err_response = [this,response](const std::string& id,const std::string& errmsg){
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            brpc::ClosureGuard rpc_guard(done);
            //1. 从请求中取出手机号码和验证码 ID，以及验证码。
            std::string phone = request->phone_number();
            std::string code_id = request->verify_code_id();
            std::string code = request->verify_code();
            //2. 检查注册手机号码是否合法
            bool ret = checkPhone(phone);
            if(ret == false)
            {
                LOG_ERROR("手机号码格式错误");
                return err_response(request->request_id(),"手机号码格式错误!");
            }
            //3. 根据手机号从数据数据进行用户信息查询，判断用用户是否存在
            auto user = _mysql_handle->select_by_phone(phone);
            if(!user)
            {
                LOG_ERROR("该手机号还没有注册 ： {}",phone);
                return err_response(request->request_id(),"该手机号还没有注册!");
            }
            //4. 从 redis 数据库中进行验证码 ID-验证码一致性匹配
            auto vcode = _code_handle->uid(code_id);
            if(vcode != code)
            {
                LOG_ERROR("验证码错误 {}-{}",phone,code);
                return err_response(request->request_id(),"验证码错误!");
            }
            _code_handle->remove(code_id);
            //5. 根据 redis 中的登录标记信息是否存在判断用户是否已经登录。
            ret = _status_handle->exist(user->user_id());
            if(ret == true)
            {
                LOG_ERROR("用户已在其他地方登录");
                return err_response(request->request_id(),"用户已在其他地方登录!");
            }
            //6. 构造会话 ID，生成会话键值对，向 redis 中添加会话信息以及登录标记信息
            std::string ssid = uuid();
            _session_handle->append(ssid,user->user_id());
            //7. 组织响应，返回生成的会话 ID
            response->set_request_id(request->request_id());
            response->set_success(true);
            response->set_login_session_id(ssid);
        }

        void GetUserInfo(google::protobuf::RpcController *controller,
                         const ::zrb::GetUserInfoReq *request,
                         ::zrb::GetUserInfoRsp *response,
                         ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            //定义一个错误回调函数
            auto err_response = [this,response](const std::string& id,const std::string& errmsg){
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1. 从请求中取出用户 ID
            std::string user_id = request->user_id();
            //2. 通过用户 ID，从数据库中查询用户信息
            auto user = _mysql_handle->select_by_user_id(user_id);
            if(!user)
            {
                LOG_ERROR("未找到{}用户信息",user_id);
                return err_response(request->request_id(),"未找到用户信息!");
            }
            //3. 根据用户信息中的头像 ID，从文件服务器获取头像文件数据，组织完整用户信息
            UserInfo* userinfo = response->mutable_user_info();
            userinfo->set_user_id(user->user_id());
            userinfo->set_nickname(user->nickname());
            userinfo->set_description(user->description());
            userinfo->set_phone(user->phone());
            if(!(user->avatar_id().empty())) //存在头像
            {
                //从信道管理对象中，获取连接到了文件管理子服务的channel
                auto channel = _mm_channel->choose(_file_name_service);
                if(!channel)
                {
                    LOG_ERROR("未找到文件子服务节点");
                    return err_response(request->request_id(),"未找到文件子服务节点!");
                }
                //进行文件子服务的rpc，请求进行文件下载
                //1.实例化rpc调用客户端对象，发起rpc调用
                zrb::FileService_Stub stub(channel.get());
                zrb::GetSingleFileReq req;
                req.set_request_id(request->request_id());
                req.set_file_id(user->avatar_id());

                brpc::Controller cntl;
                zrb::GetSingleFileRsp rsp;
                stub.GetSingleFile(&cntl, &req, &rsp, nullptr);

                if(cntl.Failed() || rsp.success() == false)
                {
                    LOG_ERROR("{} 网络子服务调用失败 - {}",request->request_id(),cntl.ErrorText());
                    return err_response(request->request_id(),"网络子服务调用失败!");
                }
                userinfo->set_avatar(rsp.file_data().file_content());
            }
            //4. 组织响应，返回用户信息
            response->set_request_id(request->request_id());
            response->set_success(true);
        }

        void GetMultiUserInfo(google::protobuf::RpcController *controller,
                              const ::zrb::GetMultiUserInfoReq *request,
                              ::zrb::GetMultiUserInfoRsp *response,
                              ::google::protobuf::Closure *done)
        {
            //定义一个错误回调函数
            auto err_response = [this,response](const std::string& id,const std::string& errmsg){
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            brpc::ClosureGuard rpc_guard(done);
            //1.从请求中取出用户ID列表
            std::vector<std::string> uid_list;
            for(int i = 0; i < request->users_id_size(); ++i)
            {
                uid_list.push_back(request->users_id(i));
            }
            //2.从数据库中进行批量用户信息查询
            auto users = _mysql_handle->select_multi_id(uid_list);
            if(users.size() != request->users_id_size())
            {
                LOG_ERROR("从数据库获取的数据与请求的数据不一致 请求的个数：{} 请求成功的个数：{}",request->users_id_size(),users.size());
                return err_response(request->request_id(),"从数据库获取的数据与请求的数据不一致!");
            }

            //3.批量从文件管理子服务进行文件下载
            auto channel = _mm_channel->choose(_file_name_service); //获取信道
            if(!channel)
            {
                LOG_ERROR("未找到文件子服务节点");
                return err_response(request->request_id(),"未找到文件子服务节点!");
            }

            zrb::FileService_Stub stub(channel.get());
            zrb::GetMultiFileReq req;
            req.set_request_id(request->request_id());
            for(auto& user : users)
            {
                if (user.avatar_id().empty()) continue; //没有头像id就不进行添加
                    req.add_file_id_list(user.avatar_id());
            }

            brpc::Controller cntl;
            zrb::GetMultiFileRsp rsp;
            stub.GetMultiFile(&cntl, &req, &rsp, nullptr);

            if(cntl.Failed() || rsp.success() == false)
            {
                LOG_ERROR("{} 网络子服务调用失败 - {}",request->request_id(),cntl.ErrorText());
                return err_response(request->request_id(),"网络子服务调用失败!");
            }

            for(auto user : users) //遍历用户信息
            {
                auto file_map = rsp.mutable_file_data(); //网络子服务调用下载的所有头像数据
                auto user_map = response->mutable_users_info();   //此次下载多个用户数据请求的响应中的存储数据位置
                UserInfo userinfo;
                userinfo.set_user_id(user.user_id());
                userinfo.set_nickname(user.nickname());
                userinfo.set_description(user.description());
                userinfo.set_phone(user.phone());
                if(!(user.avatar_id().empty())) //用户有头像，就进行在文件下载中取出
                {
                    userinfo.set_avatar((*file_map)[user.avatar_id()].file_content());
                    // std::cout << (*file_map)[user.avatar_id()].file_content() << std::endl;
                }
                (*user_map)[user.user_id()] = userinfo;
            }

            //4.组织响应
            response->set_request_id(request->request_id());
            response->set_success(true);
        }

        void SetUserAvatar(google::protobuf::RpcController *controller,
                           const ::zrb::SetUserAvatarReq *request,
                           ::zrb::SetUserAvatarRsp *response,
                           ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            //定义一个错误回调函数
            auto err_response = [this,response](const std::string& id,const std::string& errmsg){
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1. 从请求中取出用户 ID 与头像数据
            std::string uid = request->user_id();
            //2. 从数据库通过用户 ID 进行用户信息查询，判断用户是否存在
            auto user = _mysql_handle->select_by_user_id(uid);
            if(!user)
            {
                LOG_ERROR("未找到{}用户信息",uid);
                return err_response(request->request_id(),"未找到用户信息!");
            }
            //3. 上传头像文件到文件子服务，
            auto channel = _mm_channel->choose(_file_name_service); //获取信道
            if(!channel)
            {
                LOG_ERROR("未找到文件子服务节点");
                return err_response(request->request_id(),"未找到文件子服务节点!");
            }
            //实例化rpc调用客户端对象，发起rpc调用
            zrb::FileService_Stub stub(channel.get());
            zrb::PutSingleFileReq req;
            req.set_request_id(request->request_id());
            req.mutable_file_data()->set_file_name("");
            req.mutable_file_data()->set_file_size(request->avatar().size());
            req.mutable_file_data()->set_file_content(request->avatar());

            brpc::Controller cntl;
            zrb::PutSingleFileRsp rsp;
            stub.PutSingleFile(&cntl, &req, &rsp, nullptr);

            if(cntl.Failed() || rsp.success() == false)
            {
                LOG_ERROR("{} 网络子服务调用失败 - {}",request->request_id(),cntl.ErrorText());
                return err_response(request->request_id(),"网络子服务调用失败!");
            }

            std::string avatar_id = rsp.file_info().file_id();
            //4. 将返回的头像文件 ID 更新到数据库中
            user->avatar_id(avatar_id);
            bool ret = _mysql_handle->update(user);
            if(ret == false)
            {
                LOG_ERROR("{} 更新数据库失败",request->request_id());
                return err_response(request->request_id(),"更新数据库失败!");
            }
            //5. 更新 ES 服务器中用户信息
            ret = _es_handle->appendData(user->user_id(),
                user->nickname(),
                user->phone(),
                user->description(),
                user->avatar_id());
            if(ret == false)
            {
                LOG_ERROR("{} 更新es搜索引擎失败",request->request_id());
                return err_response(request->request_id(),"更新es搜索引擎失败!");
            }
            //6. 组织响应，返回更新成功与否
            response->set_request_id(request->request_id());
            response->set_success(true);
        }

        void SetUserNickname(google::protobuf::RpcController *controller,
                             const ::zrb::SetUserNicknameReq *request,
                             ::zrb::SetUserNicknameRsp *response,
                             ::google::protobuf::Closure *done)
        {

            brpc::ClosureGuard rpc_guard(done);
            //定义一个错误回调函数
            auto err_response = [this,response](const std::string& id,const std::string& errmsg){
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            // 1. 从请求中取出用户 ID 与新的昵称
            std::string user_id = request->user_id();
            std::string NewNickName = request->nickname();
            // 2. 判断昵称格式是否正确
            bool ret = checkNickname(NewNickName);
            if(ret == false)
            {
                LOG_ERROR("昵称格式不合法");
                return err_response(request->request_id(),"用户名不合法!");
            }
            // 3. 从数据库通过用户 ID 进行用户信息查询，判断用户是否存在
            auto user = _mysql_handle->select_by_user_id(user_id);
            if(!user)
            {
                LOG_ERROR("未找到{}用户信息",user_id);
                return err_response(request->request_id(),"未找到用户信息!");
            }
            // 4. 将新的昵称更新到数据库中
            user->nickname(NewNickName);
            ret = _mysql_handle->update(user);
            if(ret == false)
            {
                LOG_ERROR("{} 更新数据库失败",request->request_id());
                return err_response(request->request_id(),"更新数据库失败!");
            }
            // 5. 更新 ES 服务器中用户信息
            ret = _es_handle->appendData(user->user_id(),
                user->nickname(),
                user->phone(),
                user->description(),
                user->avatar_id());
            if(ret == false)
            {
                LOG_ERROR("{} 更新es搜索引擎失败",request->request_id());
                return err_response(request->request_id(),"更新es搜索引擎失败!");
            }
            // 6. 组织响应，返回更新成功与否
            response->set_request_id(request->request_id());
            response->set_success(true);
        }

        void SetUserDescription(google::protobuf::RpcController *controller,
                                const ::zrb::SetUserDescriptionReq *request,
                                ::zrb::SetUserDescriptionRsp *response,
                                ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            //定义一个错误回调函数
            auto err_response = [this,response](const std::string& id,const std::string& errmsg){
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1. 从请求中取出用户 ID 与新的签名
            std::string user_id = request->user_id();
            std::string NewDescription = request->description();
            //2. 从数据库通过用户 ID 进行用户信息查询，判断用户是否存在
            auto user = _mysql_handle->select_by_user_id(user_id);
            if(!user)
            {
                LOG_ERROR("未找到{}用户信息",user_id);
                return err_response(request->request_id(),"未找到用户信息!");
            }
            //3. 将新的签名更新到数据库中
            user->description(NewDescription);
            auto ret = _mysql_handle->update(user);
            if(ret == false)
            {
                LOG_ERROR("{} 更新数据库失败",request->request_id());
                return err_response(request->request_id(),"更新数据库失败!");
            }
            //4. 更新 ES 服务器中用户信息
            ret = _es_handle->appendData(user->user_id(),
                user->nickname(),
                user->phone(),
                user->description(),
                user->avatar_id());
            if(ret == false)
            {
                LOG_ERROR("{} 更新es搜索引擎失败",request->request_id());
                return err_response(request->request_id(),"更新es搜索引擎失败!");
            }
            //5. 组织响应，返回更新成功与否
            response->set_request_id(request->request_id());
            response->set_success(true);
        }

        void SetUserPhoneNumber(google::protobuf::RpcController *controller,
                                const ::zrb::SetUserPhoneNumberReq *request,
                                ::zrb::SetUserPhoneNumberRsp *response,
                                ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            //定义一个错误回调函数
            auto err_response = [this,response](const std::string& id,const std::string& errmsg){
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1. 从请求中取出手机号码和验证码 ID，以及验证码
            std::string user_id = request->user_id();
            std::string NewPhone = request->phone_number();
            std::string code = request->phone_verify_code();
            std::string code_id = request->phone_verify_code_id();
            //2. 检查注册手机号码是否合法
            bool ret = checkPhone(NewPhone);
            if(ret == false)
            {
                LOG_ERROR("手机号码格式错误");
                return err_response(request->request_id(),"手机号码格式错误!");
            }
            //3. 从 redis 数据库中进行验证码 ID-验证码一致性匹配
            auto vcode = _code_handle->uid(code_id);
            if(vcode != code)
            {
                LOG_ERROR("验证码错误 {}-{}",NewPhone,code);
                return err_response(request->request_id(),"验证码错误!");
            }
            //4. 从数据库通过用户 ID 进行用户信息查询，判断用户是否存在
            auto user = _mysql_handle->select_by_user_id(user_id);
            if(!user)
            {
                LOG_ERROR("未找到{}用户信息",user_id);
                return err_response(request->request_id(),"未找到用户信息!");
            }
            //5. 将新的手机号更新到数据库中
            user->phone(NewPhone);
            ret = _mysql_handle->update(user);
            if(ret == false)
            {
                LOG_ERROR("{} 更新数据库失败",request->request_id());
                return err_response(request->request_id(),"更新数据库失败!");
            }
            //6. 更新 ES 服务器中用户信息
            ret = _es_handle->appendData(user->user_id(),
                user->nickname(),
                user->phone(),
                user->description(),
                user->avatar_id());
            if(ret == false)
            {
                LOG_ERROR("{} 更新es搜索引擎失败",request->request_id());
                return err_response(request->request_id(),"更新es搜索引擎失败!");
            }
            //7. 组织响应，返回更新成功与否
            response->set_request_id(request->request_id());
            response->set_success(true);
        }

    private:
        //odb框架和mysql操作封装句柄

        userTable::ptr _mysql_handle;

        //es用户操作客户端
        ESUser::ptr _es_handle;

        //会话、登录状态、验证码封装的句柄
        Session::ptr _session_handle;
        Status::ptr _status_handle;
        Code::ptr _code_handle;
        ServiceManager::ptr _mm_channel; //RPC信道管理对象,进行文件子服务的rpc，请求进行头像的文件下载
        std::string _file_name_service; //文件子服务的名称
        DMSClient::ptr _dms_client;
    };

    // （2）.创建rpc服务器类，搭建服务器
    //  （3）.向服务器中添加rpc子服务对象 -- 告诉服务器收到什么请求用哪个接口处理
    class UserServer
    {
    public:
        using ptr = shared_ptr<UserServer>;
        UserServer(const Registry::ptr& service_registry, 
            const Discovery::ptr& service_discoverer, 
            const std::shared_ptr<brpc::Server> &rpc_server,
            const std::shared_ptr<odb::core::database>& mysql_odb,
            const std::shared_ptr<elasticlient::Client>& es_client,
            const std::shared_ptr<sw::redis::Redis>& redis_client)
            :_service_discoverer(service_discoverer)
            ,_service_registry(service_registry)
            ,_rpc_server(rpc_server)
            ,_mysql_odb(mysql_odb)
            ,_es_client(es_client)
            ,_redis_client(redis_client)
        {
        }

        void start()
        {
            _rpc_server->RunUntilAskedToQuit(); // 休眠等待运行结束
        }

        ~UserServer()
        {
        }

    private:
        Discovery::ptr _service_discoverer;                 //服务发现对象
        Registry::ptr _service_registry;                    // etcd服务器注册类
        std::shared_ptr<brpc::Server> _rpc_server;         // brpc服务器对象
        std::shared_ptr<odb::core::database> _mysql_odb;
        std::shared_ptr<elasticlient::Client> _es_client;
        std::shared_ptr<sw::redis::Redis> _redis_client;
    };

    // SpeechServer构造过程参数太多，使用建造者模式
    class UserServerBuild
    {
    public:
        //构造mysql客户端
        void make_mysql_object(
            int max_pool,
            const std::string& user,
            const std::string& password,
            const std::string& dbname,
            const std::string& host,
            int port,
            const std::string& cset)
        {
            _mysql_odb = ODBFactory::create(max_pool,user,password,dbname,host,port,cset);
        }

        //构造短信验证平台
        void make_dms_object(const std::string &access_key_id,
            const std::string &access_key_secret)
        {
            _dms_client = std::make_shared<DMSClient>(access_key_id,access_key_secret);
        }

        //构造es客户端
        void make_es_object(std::string& host)
        {
            _es_client = ESClientFactory::create(host);
        }

        //构造redis客户端
        void make_redis_object(
            const std::string& ip,
            int32_t port,
            int32_t db,
            bool keep_alive)
        {
            _redis_client = RedisClientFactory::create(ip,port,db,keep_alive);
        }

        //用于构造服务发现客户端&信道管理对象
        void make_discovery_object(const std::string& reg_host,
            const std::string& base_service_name,
            const std::string& file_service_name)
        {
            _file_service_name = file_service_name;
            //信道管理对象
            _mm_channel = std::make_shared<ServiceManager>();
            _mm_channel->declared(file_service_name);
            auto put_cb = std::bind(&ServiceManager::onServiceOnline, _mm_channel.get(), std::placeholders::_1, std::placeholders::_2);
            auto del_cb = std::bind(&ServiceManager::onServiceOffline, _mm_channel.get(), std::placeholders::_1, std::placeholders::_2);
            //2. 构造服务发现对象
            _service_discoverer = std::make_shared<Discovery>(reg_host, base_service_name, put_cb, del_cb);
        }

        // 构造etcd服务注册对象
        // 网络Ip、注册的服务名称、注册的服务地址
        void make_reg_object(const std::string &host, const std::string &name, const std::string &access_host)
        {
            _service_registry = std::make_shared<Registry>(host);
            _service_registry->registry(name, access_host); // 服务注册
        }
        // 构造brpc服务对象
        // 端口 超时时间 线程数量
        void make_rpc_object(uint16_t port, 
            uint32_t timeout, 
            uint8_t thread_num)
        {
            _rpc_server = std::make_shared<brpc::Server>();

            // 向服务器增加一个UserServerImpl服务，SpeechServiceImpl服务是自己实现的
            UserServiceImpl *echo_service = new UserServiceImpl(_dms_client,_mysql_odb,_es_client,_redis_client,_mm_channel,_file_service_name);
            // brpc::ServiceOwnership::SERVER_DOESNT_OWN_SERVICE 表示添加服务失败，服务器也不会删除服务对象
            // brpc::ServiceOwnership::SERVER_OWNS_SERVICE 表示添加服务失败时，服务器负责删除服务对象
            int ret = _rpc_server->AddService(echo_service, brpc::ServiceOwnership::SERVER_OWNS_SERVICE);
            if (ret == -1)
            {
                LOG_ERROR("添加Rpc服务失败！");
                abort();
            }

            // 启动服务器
            brpc::ServerOptions options;
            options.idle_timeout_sec = timeout;       // 连接空闲超时时间-超时后连接被关闭
            options.num_threads = thread_num;         // io线程数量
            ret = _rpc_server->Start(port, &options); // 监听的端口
            if (ret == -1)
            {
                LOG_ERROR("rpc服务器启动失败");
                abort();
            }
        }

        UserServer::ptr build()
        {
            if (!_mysql_odb)
            {
                LOG_ERROR("还未初始化mysql数据库模块");
                abort();
            }     
            if (!_es_client)
            {
                LOG_ERROR("还未初始化es搜索引擎模块");
                abort();
            }
            if(!_redis_client)
            {
                LOG_ERROR("还未初始化redis数据库模块");
                abort();
            }
            if(!_dms_client)
            {
                LOG_ERROR("还未初始化短信验证码模块");
                abort();
            }
            if(!_mm_channel)
            {
                LOG_ERROR("还未初始化信道管理模块");
                abort();
            }
            if(!_service_discoverer)
            {
                LOG_ERROR("还未初始化服务发现模块");
                abort();
            }
            if (!_rpc_server)
            {
                LOG_ERROR("还未初始化rpc服务模块");
                abort();
            }
            if (!_service_registry)
            {
                LOG_ERROR("还未初始化服务注册客户端模块");
                abort();
            }

            UserServer::ptr server = std::make_shared<UserServer>(_service_registry,
                _service_discoverer,
                _rpc_server,
                _mysql_odb,
                _es_client,
                _redis_client);
            return server;
        }

    private:
        std::string _file_service_name;                    //服务地址
        ServiceManager::ptr _mm_channel;                     //RPC信道管理对象
        Discovery::ptr _service_discoverer;                 //服务发现对象
        Registry::ptr _service_registry;                    // etcd服务器注册类
        std::shared_ptr<brpc::Server> _rpc_server;         // brpc服务器对象
        std::shared_ptr<odb::core::database> _mysql_odb;
        std::shared_ptr<elasticlient::Client> _es_client;
        std::shared_ptr<sw::redis::Redis> _redis_client;
        DMSClient::ptr _dms_client;                        //短信验证码客户端
    };
};
